//@version=5
indicator("CHOCH & BOS (MACD Swing-Based) MTF + Trendlines", shorttitle="MACD Market Structure", overlay=false, max_lines_count=500, max_labels_count=500, max_boxes_count=500)

// ==========================================
// ─── INPUTS ───
// ==========================================

// --- MACD Settings (Shared) ---
grp_macd = "Global MACD Settings"
fast_len = input.int(12, title="Fast Length", group=grp_macd)
slow_len = input.int(26, title="Slow Length", group=grp_macd)
sig_len = input.int(9, title="Signal Length", group=grp_macd)
nearby_len = input.int(5, title="Nearby Lookback", group=grp_macd, tooltip="Lookback bars of the SPECIFIC timeframe (e.g., 5 H1 bars) to find the peak.")

// --- Timeframe 1 Settings (Primary - Chart) ---
grp_tf1 = "Timeframe 1 (Primary)"
tf1_on = input.bool(true, "Show Timeframe 1", group=grp_tf1)
tf1_input = input.timeframe("", title="TF 1 Selection", group=grp_tf1) 
tf1_width = input.int(2, "TF 1 Line Width", group=grp_tf1)
col_bull_1 = input.color(color.green, "TF 1 Bull Color", group=grp_tf1)
col_bear_1 = input.color(color.red, "TF 1 Bear Color", group=grp_tf1)
// New Input for Trendlines
col_trend = input.color(color.new(color.purple, 30), "TF 1 Trendline Color", group=grp_tf1)

// --- Entry Settings (Sniper) ---
grp_entry = "Sniper Entry Settings"
entry_tf = input.timeframe("1", "Entry Confirmation TF (Default M1)", group=grp_entry, tooltip="The timeframe used for the MACD Zero Cross trigger after the zone is touched.")

// --- Volume Profile Settings ---
grp_vp = "Fixed Range Volume Profile (TF1 Only)"
show_vp = input.bool(true, "Show VP on CHoCH", group=grp_vp)
vp_rows = input.int(24, "VP Rows", minval=5, maxval=100, group=grp_vp)
vp_width_pct = input.int(50, "VP Width % (of Time Range)", minval=10, maxval=100, group=grp_vp)
col_vp = input.color(color.new(color.blue, 60), "VP Color", group=grp_vp)
col_vp_poc = input.color(color.new(color.yellow, 30), "VP POC Color / Zone", group=grp_vp)

// --- Timeframe 2 Settings (Default OFF) ---
grp_tf2 = "Timeframe 2"
tf2_on = input.bool(false, "Enable Timeframe 2", group=grp_tf2) 
tf2_input = input.timeframe("5", title="TF 2 Selection", group=grp_tf2)
tf2_width = input.int(1, "TF 2 Line Width", group=grp_tf2)
col_bull_2 = input.color(#4caf50, "TF 2 Bull Color", group=grp_tf2) 
col_bear_2 = input.color(#ef5350, "TF 2 Bear Color", group=grp_tf2) 

// --- Timeframe 3 Settings (Default OFF) ---
grp_tf3 = "Timeframe 3"
tf3_on = input.bool(false, "Enable Timeframe 3", group=grp_tf3) 
tf3_input = input.timeframe("15", title="TF 3 Selection", group=grp_tf3)
tf3_width = input.int(1, "TF 3 Line Width", group=grp_tf3)
col_bull_3 = input.color(#00897b, "TF 3 Bull Color", group=grp_tf3) 
col_bear_3 = input.color(#c62828, "TF 3 Bear Color", group=grp_tf3) 

// --- Timeframe 4 Settings (Default OFF) ---
grp_tf4 = "Timeframe 4"
tf4_on = input.bool(false, "Enable Timeframe 4", group=grp_tf4) 
tf4_input = input.timeframe("240", title="TF 4 Selection", group=grp_tf4)
tf4_width = input.int(1, "TF 4 Line Width", group=grp_tf4)
col_bull_4 = input.color(#7cb342, "TF 4 Bull Color", group=grp_tf4) 
col_bear_4 = input.color(#d84315, "TF 4 Bear Color", group=grp_tf4) 

grp_lbl = "Labels & Dashboard"
show_labels = input.bool(true, "Show CHoCH / BoS Labels", group=grp_lbl)
show_swings = input.bool(true, "Show Swing Labels (SH/SL)", group=grp_lbl)
show_dash = input.bool(true, "Show Status Dashboard", group=grp_lbl)
show_fibo = input.bool(true, "Show Fibo TimeZone (TF1 Only)", group=grp_lbl, tooltip="Draws Fibo Time Zones when CHOCH occurs on Primary Timeframe")

// Helper to generate TF string label
get_tf_label(string tf) =>
    switch tf
        "1" => "M1"
        "3" => "M3"
        "5" => "M5"
        "15" => "M15"
        "30" => "M30"
        "45" => "M45"
        "60" => "H1"
        "120" => "H2"
        "180" => "H3"
        "240" => "H4"
        "360" => "H6"
        "720" => "H12"
        "D" => "D1"
        "1D" => "D1"
        "W" => "W1"
        "1W" => "W1"
        "M" => "MN"
        "1M" => "MN"
        => tf 

// ==========================================
// ─── TYPE DEFINITION ───
// ==========================================
type StrucState
    // Trackers for MACD Peak (Current being formed)
    float peak_macd_high = -10e9
    float price_peak_high = na 
    int idx_peak_high = na
    
    float peak_macd_low = 10e9
    float price_peak_low = na 
    int idx_peak_low = na

    // --- NEW: Trackers for PREVIOUS Confirmed Peaks (for trendlines) ---
    float prev_price_peak_high = na
    int prev_idx_peak_high = na
    float prev_price_peak_low = na
    int prev_idx_peak_low = na
    
    // Current Market Trend (1=Bull, -1=Bear)
    int trend = 0
    string last_sig_type = "Waiting" 
    
    // Active Horizontal Lines
    line active_res_line = na
    float active_res_lvl = na
    line active_sup_line = na
    float active_sup_lvl = na

    // --- NEW: Active Trendlines & Labels ---
    line line_sh_trend = na
    line line_sl_trend = na
    label lb_trend_res = na
    label lb_trend_sup = na

    // Fibo Tracking
    int idx_fibo_start = na
    int idx_fibo_end = na
    line[] fib_lines = na
    label[] fib_labels = na
    
    // Volume Profile & Zone Tracking
    box[] vp_boxes = na
    float poc_zone_top = na
    float poc_zone_btm = na
    bool zone_touched = false // Has price revisited the zone?
    bool entry_signal_given = false // Ensure we only signal once per sequence
    
    // Alert Flags
    bool alert_touch_now = false // Trigger for Zone Touch Alert
    
    // Signal Flags (Read by global plotter)
    bool buy_signal = false
    bool sell_signal = false

// ==========================================
// ─── DATA FETCHING ───
// ==========================================

// Function to calculate data in ANY context (Chart or Security)
calc_data(int f_len, int s_len, int si_len, int nb_len) =>
    [m, s, h] = ta.macd(close, f_len, s_len, si_len)
    hn = ta.highest(high, nb_len)
    ln = ta.lowest(low, nb_len)
    // Return: MACD, Signal, Hist, Close, High, Low, HighNear, LowNear
    [m, s, h, close, high, low, hn, ln]

// 1. Calculate for CURRENT CHART (Live Data)
[cur_m, cur_s, cur_h, cur_c, cur_hi, cur_lo, cur_hn, cur_ln] = calc_data(fast_len, slow_len, sig_len, nearby_len)

// 2. Calculate for SECURITY (Strict HTF Data - Non Repainting)
[s1_m, s1_s, s1_h, s1_c, s1_hi, s1_lo, s1_hn, s1_ln] = request.security(syminfo.tickerid, tf1_input, calc_data(fast_len, slow_len, sig_len, nearby_len), lookahead = barmerge.lookahead_off)
[s2_m, s2_s, s2_h, s2_c, s2_hi, s2_lo, s2_hn, s2_ln] = request.security(syminfo.tickerid, tf2_input, calc_data(fast_len, slow_len, sig_len, nearby_len), lookahead = barmerge.lookahead_off)
[s3_m, s3_s, s3_h, s3_c, s3_hi, s3_lo, s3_hn, s3_ln] = request.security(syminfo.tickerid, tf3_input, calc_data(fast_len, slow_len, sig_len, nearby_len), lookahead = barmerge.lookahead_off)
[s4_m, s4_s, s4_h, s4_c, s4_hi, s4_lo, s4_hn, s4_ln] = request.security(syminfo.tickerid, tf4_input, calc_data(fast_len, slow_len, sig_len, nearby_len), lookahead = barmerge.lookahead_off)

// 3. FETCH ENTRY MACD (M1 by default)
[entry_m, entry_s, entry_h] = request.security(syminfo.tickerid, entry_tf, ta.macd(close, fast_len, slow_len, sig_len))

// 4. CALCULATE ENTRY CROSSOVERS (Global Scope)
bool entry_cross_up = ta.crossover(entry_m, 0)
bool entry_cross_down = ta.crossunder(entry_m, 0)

// 5. SELECT Correct Data Source
[m1, s1, h1, c1, hi1, lo1, hn1, ln1] = if tf1_input == "" or tf1_input == timeframe.period
    [cur_m, cur_s, cur_h, cur_c, cur_hi, cur_lo, cur_hn, cur_ln] 
else 
    [s1_m, s1_s, s1_h, s1_c, s1_hi, s1_lo, s1_hn, s1_ln]

[m2, s2, h2, c2, hi2, lo2, hn2, ln2] = if tf2_input == timeframe.period
    [cur_m, cur_s, cur_h, cur_c, cur_hi, cur_lo, cur_hn, cur_ln] 
else 
    [s2_m, s2_s, s2_h, s2_c, s2_hi, s2_lo, s2_hn, s2_ln]

[m3, s3, h3, c3, hi3, lo3, hn3, ln3] = if tf3_input == timeframe.period
    [cur_m, cur_s, cur_h, cur_c, cur_hi, cur_lo, cur_hn, cur_ln] 
else 
    [s3_m, s3_s, s3_h, s3_c, s3_hi, s3_lo, s3_hn, s3_ln]

[m4, s4, h4, c4, hi4, lo4, hn4, ln4] = if tf4_input == timeframe.period
    [cur_m, cur_s, cur_h, cur_c, cur_hi, cur_lo, cur_hn, cur_ln] 
else 
    [s4_m, s4_s, s4_h, s4_c, s4_hi, s4_lo, s4_hn, s4_ln]


// ==========================================
// ─── DRAWING HELPERS ───
// ==========================================

// Function to draw Fibo Time Zones (Manages Cleanup too)
draw_fibo_zone(StrucState state, int start_idx, int end_idx) =>
    // 1. Clean up old drawings (Crucial for "latest CHOCH" logic)
    if array.size(state.fib_lines) > 0
        for l in state.fib_lines
            line.delete(l)
        array.clear(state.fib_lines)
    
    if array.size(state.fib_labels) > 0
        for lb in state.fib_labels
            label.delete(lb)
        array.clear(state.fib_labels)

    // 2. Draw New Drawings
    int dist = end_idx - start_idx
    int[] fibs = array.from(3, 5, 8, 13, 21, 34) 
    
    // Draw Connector Line (Start to End)
    line l_conn = line.new(start_idx, close, end_idx, close, color=color.new(color.blue, 50), style=line.style_dotted, force_overlay=true)
    array.push(state.fib_lines, l_conn)
    
    for i = 0 to array.size(fibs) - 1
        int fib = array.get(fibs, i)
        int target_idx = start_idx + (dist * fib)
        
        // SAFETY CHECK: Ensure we don't draw beyond TradingView's 500-bar future limit
        if target_idx <= bar_index + 500
            // Draw Vertical Line
            line l_vert = line.new(target_idx, close * 0.5, target_idx, close * 1.5, extend=extend.both, color=color.new(#2962FF, 50), style=line.style_solid, force_overlay=true, width=1)
            array.push(state.fib_lines, l_vert)
            
            // Draw Label
            label lb_fib = label.new(target_idx, high, str.tostring(fib), color=color.new(#2962FF, 100), textcolor=#2962FF, style=label.style_none, yloc=yloc.abovebar, force_overlay=true)
            array.push(state.fib_labels, lb_fib)

// Function to Draw Fixed Range Volume Profile
draw_fixed_vp(StrucState state, int start_idx, int end_idx, float top_price, float bottom_price) =>
    // 1. Validation
    if end_idx > start_idx and top_price > bottom_price
        // Define Range
        float range_height = top_price - bottom_price
        float row_step = range_height / vp_rows
        
        // Initialize Volume Array
        float[] vol_buckets = array.new_float(vp_rows, 0.0)
        float max_vol = 0.0
        int poc_idx = 0
        
        // 2. Iterate Bars in Range to Calculate Profile
        // We loop backwards from current bar (end_idx) to start_idx
        int lookback = end_idx - start_idx
        
        for i = 0 to lookback
            // Get historical data safely
            float h_i = high[i]
            float l_i = low[i]
            float v_i = volume[i]
            
            if not na(v_i) and not na(h_i) and not na(l_i)
                // Distribute volume across overlapping rows
                // Simple logic: if bar overlaps row, add proportional volume
                for r = 0 to vp_rows - 1
                    float r_btm = bottom_price + (r * row_step)
                    float r_top = r_btm + row_step
                    
                    // Check overlap
                    if h_i >= r_btm and l_i <= r_top
                        // Add volume
                        array.set(vol_buckets, r, array.get(vol_buckets, r) + v_i)
        
        // 3. Find POC (Max Volume)
        for r = 0 to vp_rows - 1
            float v = array.get(vol_buckets, r)
            if v > max_vol
                max_vol := v
                poc_idx := r
                
        // 4. Draw Histogram
        int time_width = end_idx - start_idx
        
        for r = 0 to vp_rows - 1
            float v = array.get(vol_buckets, r)
            if v > 0
                // Calculate Width in bars based on max volume
                // We use the user defined width % relative to the time range
                float relative_width = (v / max_vol) * (time_width * (vp_width_pct / 100.0))
                
                // Coordinates & Styling
                bool is_poc = r == poc_idx
                
                int b_left = start_idx
                // Regular bars end at relative width, POC extends
                int b_right_coord = is_poc ? bar_index + 1 : start_idx + math.round(relative_width)
                // Ensure min width of 1 for regular bars
                if not is_poc and b_right_coord <= b_left
                    b_right_coord := b_left + 1
                    
                float b_btm = bottom_price + (r * row_step)
                float b_top = b_btm + row_step

                // Determine Color & Extension (Highlight POC)
                color bg = is_poc ? col_vp_poc : col_vp
                // Make POC box wider border and distinct color
                int b_width = is_poc ? 2 : 0
                color b_col = is_poc ? color.new(color.yellow, 0) : na
                // Extension setting
                string ext_type = is_poc ? extend.right : extend.none
                
                // Draw Box (TradingView garbage collector will handle deletion)
                box.new(b_left, b_top, b_right_coord, b_btm, border_width=b_width, border_color=b_col, bgcolor=bg, extend=ext_type, force_overlay=true)

                // Draw POC Price Label
                if is_poc
                    float poc_price = (b_top + b_btm) / 2
                    // Update State with new POC Zone details
                    state.poc_zone_top := b_top
                    state.poc_zone_btm := b_btm
                    state.zone_touched := false // Reset touch flag for new zone
                    state.entry_signal_given := false // Reset signal flag
                    
                    // Draw label shifted right (bar_index + 5) with price
                    label.new(bar_index + 5, poc_price, "POC: " + str.tostring(poc_price, format.mintick), color=color.new(color.yellow, 0), textcolor=color.black, style=label.style_label_left, yloc=yloc.price, force_overlay=true)


// Modified to accept pre-calculated entry crossovers
update_structure(StrucState state, float macd_val, float tf_close, float tf_high_near, float tf_low_near, bool enabled, string lbl_txt, int l_width, string l_style_str, color c_bull, color c_bear, bool is_primary, string chart_tf, string tf_input_str, bool m1_x_up, bool m1_x_down) =>
    // --- CALCULATIONS (Structure Based on Current TF MACD) ---
    bool x_up = ta.crossover(macd_val, 0)
    bool x_down = ta.crossunder(macd_val, 0)
    
    // Reset signals for current bar calculation
    state.buy_signal := false
    state.sell_signal := false
    state.alert_touch_now := false // Reset alert flag every bar
    
    // Check if the current bar is a confirmed candle on the HTF
    bool is_htf_confirmed = tf_input_str == chart_tf or barstate.isconfirmed

    if enabled
        string style_to_use = l_style_str == "solid" ? line.style_solid : (l_style_str == "dashed" ? line.style_dashed : line.style_dotted)

        // --- TRACKING PEAKS ---
        if macd_val > 0
            if x_up
                state.peak_macd_high := macd_val
                state.price_peak_high := tf_high_near
                state.idx_peak_high := bar_index
            else
                if macd_val >= state.peak_macd_high
                    state.peak_macd_high := macd_val
                    state.price_peak_high := tf_high_near
                    state.idx_peak_high := bar_index
        
        if macd_val < 0
            if x_down
                state.peak_macd_low := macd_val
                state.price_peak_low := tf_low_near
                state.idx_peak_low := bar_index
            else
                if macd_val <= state.peak_macd_low
                    state.peak_macd_low := macd_val
                    state.price_peak_low := tf_low_near
                    state.idx_peak_low := bar_index

        // --- CONFIRMING SWINGS ---
        
        if x_down and is_htf_confirmed
            // A Swing High is confirmed.

            // --- NEW: DRAW TRENDLINE (Primary TF only) ---
            // If we have a previous peak recorded, connect it to the current one.
            if is_primary and not na(state.prev_price_peak_high)
                line.delete(state.line_sh_trend) // Clean up old trendline
                state.line_sh_trend := line.new(state.prev_idx_peak_high, state.prev_price_peak_high, state.idx_peak_high, state.price_peak_high, extend=extend.right, color=col_trend, width=2, force_overlay=true)
            
            // Shift current confirmed peak to previous history for next time
            state.prev_price_peak_high := state.price_peak_high
            state.prev_idx_peak_high := state.idx_peak_high
            // ---------------------------------------------

            float val = state.price_peak_high
            int idx = state.idx_peak_high
            
            state.active_res_line := line.new(idx, val, bar_index, val, color=c_bear, width=l_width, style=style_to_use, force_overlay=true)
            state.active_res_lvl := val
            
            if show_swings
                label.new(idx, val, "SH " + lbl_txt, color=color.new(c_bear, 80), textcolor=c_bear, style=label.style_circle, yloc=yloc.price, size=size.tiny, force_overlay=true)

        if x_up and is_htf_confirmed
            // A Swing Low is confirmed.

            // --- NEW: DRAW TRENDLINE (Primary TF only) ---
            // If we have a previous valley recorded, connect it to the current one.
            if is_primary and not na(state.prev_price_peak_low)
                line.delete(state.line_sl_trend) // Clean up old trendline
                state.line_sl_trend := line.new(state.prev_idx_peak_low, state.prev_price_peak_low, state.idx_peak_low, state.price_peak_low, extend=extend.right, color=col_trend, width=2, force_overlay=true)
            
            // Shift current confirmed valley to previous history for next time
            state.prev_price_peak_low := state.price_peak_low
            state.prev_idx_peak_low := state.idx_peak_low
            // ---------------------------------------------

            float val = state.price_peak_low
            int idx = state.idx_peak_low
            
            state.active_sup_line := line.new(idx, val, bar_index, val, color=c_bull, width=l_width, style=style_to_use, force_overlay=true)
            state.active_sup_lvl := val
            
            if show_swings
                label.new(idx, val, "SL " + lbl_txt, color=color.new(c_bull, 80), textcolor=c_bull, style=label.style_circle, yloc=yloc.price, size=size.tiny, force_overlay=true)

        // --- BREAKOUT CHECK ---
        
        bool is_choch_event = false

        if not na(state.active_res_line)
            if ta.crossover(tf_close, state.active_res_lvl)
                string txt = "BoS"
                // CHOCH TRIGGER (Bullish)
                if state.trend != 1
                    txt := "CHoCH"
                    is_choch_event := true
                
                state.trend := 1 
                state.last_sig_type := txt
                
                if show_labels
                    label.new(bar_index, state.active_res_lvl, txt + " " + lbl_txt, color=c_bull, textcolor=color.white, style=label.style_label_up, yloc=yloc.price, force_overlay=true)
                
                line.set_x2(state.active_res_line, bar_index)
                state.active_res_line := na
                state.active_res_lvl := na
            else
                line.set_x2(state.active_res_line, bar_index)
                float(na)
                
        if not na(state.active_sup_line)
            if ta.crossunder(tf_close, state.active_sup_lvl)
                string txt = "BoS"
                // CHOCH TRIGGER (Bearish)
                if state.trend != -1
                    txt := "CHoCH"
                    is_choch_event := true
                
                state.trend := -1 
                state.last_sig_type := txt
                
                if show_labels
                    label.new(bar_index, state.active_sup_lvl, txt + " " + lbl_txt, color=c_bear, textcolor=color.white, style=label.style_label_down, yloc=yloc.price, force_overlay=true)
                
                line.set_x2(state.active_sup_line, bar_index)
                state.active_sup_line := na
                state.active_sup_lvl := na
            else
                line.set_x2(state.active_sup_line, bar_index)
                float(na)

        // --- DRAWINGS (Triggered ONLY on CHOCH) ---
        if is_primary and is_choch_event
            if state.trend == 1 // Bullish CHOCH (Break Resistance)
                // 1. Fibo from SL to Breakout
                if show_fibo
                    state.idx_fibo_start := state.idx_peak_low
                    state.idx_fibo_end := bar_index
                    draw_fibo_zone(state, state.idx_fibo_start, state.idx_fibo_end)
                
                // 2. Fixed Range Volume Profile
                if show_vp
                    draw_fixed_vp(state, state.idx_peak_low, bar_index, high, state.price_peak_low)

            else if state.trend == -1 // Bearish CHOCH (Break Support)
                // 1. Fibo from SH to Breakout
                if show_fibo
                    state.idx_fibo_start := state.idx_peak_high
                    state.idx_fibo_end := bar_index
                    draw_fibo_zone(state, state.idx_fibo_start, state.idx_fibo_end)

                // 2. Fixed Range Volume Profile
                if show_vp
                    draw_fixed_vp(state, state.idx_peak_high, bar_index, state.price_peak_high, low)

    // --- ZONE TOUCH & ENTRY LOGIC (Primary TF) ---
    // Run on every confirmed bar for Primary TF
    if is_primary and barstate.isconfirmed and not na(state.poc_zone_top)
        // 1. CHECK IF ZONE TOUCHED
        if not state.zone_touched
            // Check if current high/low intersects with the POC Zone
            if high >= state.poc_zone_btm and low <= state.poc_zone_top
                state.zone_touched := true
                state.alert_touch_now := true // Trigger Alert Flag
                
        // 2. CHECK FOR MOMENTUM ENTRY (Using M1 MACD Cross)
        if state.zone_touched and not state.entry_signal_given
            // Bullish Scenario: Trend Up, Zone Touched, M1 MACD Crosses UP
            // We use m1_x_up (passed from global scope)
            if state.trend == 1 and m1_x_up
                state.buy_signal := true
                state.entry_signal_given := true // Only one signal per zone sequence
                
            // Bearish Scenario: Trend Down, Zone Touched, M1 MACD Crosses DOWN
            // We use m1_x_down (passed from global scope)
            if state.trend == -1 and m1_x_down
                state.sell_signal := true
                state.entry_signal_given := true // Only one signal per zone sequence
    
    // --- NEW: DISPLAY TRENDLINE PRICE LABELS (Primary TF only) ---
    if is_primary
        // Update Resistance Trendline Label
        if not na(state.line_sh_trend)
            float current_val = line.get_price(state.line_sh_trend, bar_index)
            label.delete(state.lb_trend_res) // Delete old
            state.lb_trend_res := label.new(bar_index + 5, current_val, str.tostring(current_val, format.mintick), color=color.new(color.white, 100), textcolor=c_bear, style=label.style_text_outline, force_overlay=true)

        // Update Support Trendline Label
        if not na(state.line_sl_trend)
            float current_val = line.get_price(state.line_sl_trend, bar_index)
            label.delete(state.lb_trend_sup) // Delete old
            state.lb_trend_sup := label.new(bar_index + 5, current_val, str.tostring(current_val, format.mintick), color=color.new(color.white, 100), textcolor=c_bull, style=label.style_text_outline, force_overlay=true)

    // Returning dummy value to satisfy function requirements
    float(na)


// ==========================================
// ─── EXECUTION ───
// ==========================================

// Factory function to initialize state with arrays
init_state() =>
    s = StrucState.new()
    s.fib_lines := array.new_line()
    s.fib_labels := array.new_label()
    s.vp_boxes := array.new_box() // Initialize VP boxes
    s

var state1 = init_state()
var state2 = init_state()
var state3 = init_state()
var state4 = init_state()

// Pass the current chart timeframe (timeframe.period) and the target timeframe string
// Note: We resolve tf1_input to a real string (e.g. "60") if it's empty, for label purposes
string tf1_resolved = tf1_input == "" ? timeframe.period : tf1_input

// IMPORTANT: We pass entry_cross_up / entry_cross_down (booleans from Global Scope)
update_structure(state1, m1, c1, hn1, ln1, tf1_on, get_tf_label(tf1_resolved), tf1_width, "solid", col_bull_1, col_bear_1, true, timeframe.period, tf1_resolved, entry_cross_up, entry_cross_down)
update_structure(state2, m2, c2, hn2, ln2, tf2_on, get_tf_label(tf2_input), tf2_width, "dashed", col_bull_2, col_bear_2, false, timeframe.period, tf2_input, false, false)
update_structure(state3, m3, c3, hn3, ln3, tf3_on, get_tf_label(tf3_input), tf3_width, "dotted", col_bull_3, col_bear_3, false, timeframe.period, tf3_input, false, false)
update_structure(state4, m4, c4, hn4, ln4, tf4_on, get_tf_label(tf4_input), tf4_width, "solid", col_bull_4, col_bear_4, false, timeframe.period, tf4_input, false, false)


// ==========================================
// ─── GLOBAL SIGNAL PLOTTING ───
// ==========================================
// We check the signals from 'state1' (Primary Timeframe)
// plotshape(state1.buy_signal, title="Sniper Buy Entry", style=shape.cross, location=location.belowbar, color=color.green, size=size.normal, text="🎯 BUY", textcolor=color.white, force_overlay=true)
// plotshape(state1.sell_signal, title="Sniper Sell Entry", style=shape.cross, location=location.abovebar, color=color.red, size=size.normal, text="🎯 SELL", textcolor=color.white, force_overlay=true)


// ==========================================
// ─── DASHBOARD TABLE ───
// ==========================================

var tbl = table.new(position.top_right, 1, 8, bgcolor=color.new(color.black, 60), frame_color=color.gray, frame_width=1)

if show_dash and barstate.islast
    int row = 0
    
    // --- DETERMINE BIGGEST TIMEFRAME TREND ---
    int main_trend_val = state1.trend
    // Resolve the label for dashboard header too
    string main_tf_str = get_tf_label(tf1_input == "" ? timeframe.period : tf1_input)
    
    if tf2_on
        main_trend_val := state2.trend
        main_tf_str := get_tf_label(tf2_input)
    if tf3_on
        main_trend_val := state3.trend
        main_tf_str := get_tf_label(tf3_input)
    if tf4_on
        main_trend_val := state4.trend
        main_tf_str := get_tf_label(tf4_input)
        
    // Header
    color c_trend = main_trend_val == 1 ? color.green : (main_trend_val == -1 ? color.red : color.gray)
    string t_txt = "Trend (" + main_tf_str + "): " + (main_trend_val == 1 ? "UpTrend" : (main_trend_val == -1 ? "DownTrend" : "Neutral"))
    table.cell(tbl, 0, row, t_txt, text_color=c_trend, text_size=size.normal, text_halign=text.align_left)
    row := row + 1

    // TF1 Status
    if tf1_on
        color c_t1 = state1.trend == 1 ? color.green : (state1.trend == -1 ? color.red : color.gray)
        string s1_dir = state1.trend == 1 ? "Bullish" : (state1.trend == -1 ? "Bearish" : "---")
        // Use latest structure type (BoS or CHoCH)
        string s1_txt = s1_dir + ": " + (state1.trend != 0 ? state1.last_sig_type : "Waiting") + " " + get_tf_label(tf1_input == "" ? timeframe.period : tf1_input)
        table.cell(tbl, 0, row, s1_txt, text_color=c_t1, text_size=size.small, text_halign=text.align_left)
        row := row + 1

    // TF2 Status
    if tf2_on
        color c_t2 = state2.trend == 1 ? color.green : (state2.trend == -1 ? color.red : color.gray)
        string s2_dir = state2.trend == 1 ? "Bullish" : (state2.trend == -1 ? "Bearish" : "---")
        string s2_txt = s2_dir + ": " + (state2.trend != 0 ? state2.last_sig_type : "Waiting") + " " + get_tf_label(tf2_input)
        table.cell(tbl, 0, row, s2_txt, text_color=c_t2, text_size=size.small, text_halign=text.align_left)
        row := row + 1
        
    // TF3 Status
    if tf3_on
        color c_t3 = state3.trend == 1 ? color.green : (state3.trend == -1 ? color.red : color.gray)
        string s3_dir = state3.trend == 1 ? "Bullish" : (state3.trend == -1 ? "Bearish" : "---")
        string s3_txt = s3_dir + ": " + (state3.trend != 0 ? state3.last_sig_type : "Waiting") + " " + get_tf_label(tf3_input)
        table.cell(tbl, 0, row, s3_txt, text_color=c_t3, text_size=size.small, text_halign=text.align_left)
        row := row + 1
        
    // TF4 Status
    if tf4_on
        color c_t4 = state4.trend == 1 ? color.green : (state4.trend == -1 ? color.red : color.gray)
        string s4_dir = state4.trend == 1 ? "Bullish" : (state4.trend == -1 ? "Bearish" : "---")
        string s4_txt = s4_dir + ": " + (state4.trend != 0 ? state4.last_sig_type : "Waiting") + " " + get_tf_label(tf4_input)
        table.cell(tbl, 0, row, s4_txt, text_color=c_t4, text_size=size.small, text_halign=text.align_left)
        row := row + 1

// ==========================================
// ─── MACD PLOTTING (SEPARATE PANE) ───
// ==========================================

col_macd_line = cur_m > 0 ? color.new(color.green, 50) : color.new(color.red, 50)
plot(cur_m, title="MACD Line as Histogram", style=plot.style_columns, color=col_macd_line)
hline(0, "Zero Line", color=color.gray)

// ==========================================
// ─── ALERTS ───
// ==========================================

alertcondition(state1.alert_touch_now, title="POC Zone Touched", message="Price has reached the POC Zone!")
alertcondition(state1.buy_signal, title="Sniper BUY Signal", message="Sniper BUY: Zone Touched + M1 Momentum Cross!")
alertcondition(state1.sell_signal, title="Sniper SELL Signal", message="Sniper SELL: Zone Touched + M1 Momentum Cross!")